﻿namespace AbpPlusPlus.Extentions.Date
{
    using System;
    using System.Collections;
    using System.Globalization;
    using System.Threading.Tasks;

    public static class DateTimeExtention
    {
        /// <summary>
        /// 中国农历
        /// </summary>
        private static ChineseLunisolarCalendar china = new ChineseLunisolarCalendar();

        /// <summary>
        /// 公历假日
        /// </summary>
        private static Hashtable gregorianCalendarHoliday = new Hashtable();

        /// <summary>
        /// 农历假日
        /// </summary>
        private static Hashtable lunarCalendarHoliday = new Hashtable();

        private static string[] JQ = { "小寒", "大寒", "立春", "雨水", "惊蛰", "春分", "清明", "谷雨", "立夏", "小满", "芒种", "夏至", "小暑", "大暑", "立秋", "处暑", "白露", "秋分", "寒露", "霜降", "立冬", "小雪", "大雪", "冬至" };

        private static int[] JQData = { 0, 21208, 43467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, 419210, 440795, 462224, 483532, 504758 };


        static DateTimeExtention()
        {
            // 公历节日
            gregorianCalendarHoliday.Add("0101", "元旦");
            gregorianCalendarHoliday.Add("0214", "情人节");
            gregorianCalendarHoliday.Add("0305", "雷锋日");
            gregorianCalendarHoliday.Add("0308", "妇女节");
            gregorianCalendarHoliday.Add("0312", "植树节");
            gregorianCalendarHoliday.Add("0315", "消费者权益日");
            gregorianCalendarHoliday.Add("0401", "愚人节");
            gregorianCalendarHoliday.Add("0501", "劳动节");
            gregorianCalendarHoliday.Add("0504", "青年节");
            gregorianCalendarHoliday.Add("0601", "儿童节");
            gregorianCalendarHoliday.Add("0701", "建党节");
            gregorianCalendarHoliday.Add("0801", "建军节");
            gregorianCalendarHoliday.Add("0910", "教师节");
            gregorianCalendarHoliday.Add("1001", "国庆节");
            gregorianCalendarHoliday.Add("1224", "平安夜");
            gregorianCalendarHoliday.Add("1225", "圣诞节");

            // 农历节日
            lunarCalendarHoliday.Add("0101", "春节");
            lunarCalendarHoliday.Add("0115", "元宵节");
            lunarCalendarHoliday.Add("0505", "端午节");
            lunarCalendarHoliday.Add("0707", "七夕情人节");
            lunarCalendarHoliday.Add("0715", "中元节");
            lunarCalendarHoliday.Add("0815", "中秋节");
            lunarCalendarHoliday.Add("0909", "重阳节");
            lunarCalendarHoliday.Add("1208", "腊八节");
            lunarCalendarHoliday.Add("1223", "北方小年(扫房)");
            lunarCalendarHoliday.Add("1224", "南方小年(掸尘)");
        }


        /// <summary>
        /// 获取农历
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static DateTime GetChinaDate(this DateTime value)
        {
            if (value > china.MaxSupportedDateTime || value < china.MinSupportedDateTime)
            {
                throw new Exception(string.Format("日期超出范围！必须在{0}到{1}之间！", china.MinSupportedDateTime.ToString("yyyy-MM-dd"), china.MaxSupportedDateTime.ToString("yyyy-MM-dd")));
            }

            var year = china.GetYear(value);
            var month = GetChinaMonth(value);
            var day = china.GetDayOfMonth(value);

            return new DateTime(year, month, day);
        }

        public static async Task<DateTime> GetChinaDateAsync(this DateTime value)
        {
            return await Task.FromResult(value.GetChinaDate());
        }

        /// <summary>
        /// 是公历节假日
        /// </summary>
        /// <returns></returns>
        public static bool IsGregorianCalendarHoliday(this DateTime value)
        {
            object g = gregorianCalendarHoliday[value.Month.ToString("00") + value.Day.ToString("00")];

            return g != null;
        }

        /// <summary>
        /// 是公历节假日
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static async Task<bool> IsGregorianCalendarHolidayAsync(this DateTime value)
        {
            return await Task.FromResult(value.IsGregorianCalendarHoliday());
        }

        /// <summary>
        /// 公历日期是否为指定月、日的公历假日
        /// </summary>
        /// <param name="value">公历日期</param>
        /// <param name="month">公历月</param>
        /// <param name="day">公历日</param>
        /// <returns></returns>
        public static bool IsGregorianCalendarHoliday(this DateTime value, int month, int day)
        {
            return value.Month == month && value.Day == day;
        }

        /// <summary>
        /// 公历日期是否为指定公历月和日的公历假日
        /// </summary>
        /// <param name="value">公历日期</param>
        /// <param name="month">公历月</param>
        /// <param name="day">公历日</param>
        /// <returns></returns>
        public static async Task<bool> IsGregorianCalendarHolidayAsync(this DateTime value, int month, int day)
        {
            return await Task.FromResult(value.IsGregorianCalendarHoliday(month, day));
        }

        /// <summary>
        /// 公历日期是否为指定农历月和日的农历假日
        /// </summary>
        /// <param name="value">公历日期</param>
        /// <param name="month">农历月</param>
        /// <param name="day">农历日</param>
        /// <returns></returns>
        public static bool IsLunarCalendarHoliday(this DateTime value, int month, int day)
        {
            var lunar = value.GetChinaDate();
            return lunar.Month == month && lunar.Day == day;
        }

        /// <summary>
        /// 公历日期是否为指定农历月和日的农历假日
        /// </summary>
        /// <param name="value">公历日期</param>
        /// <param name="month">农历月</param>
        /// <param name="day">农历日</param>
        /// <returns></returns>
        public static async Task<bool> IsLunarCalendarHolidayAsync(this DateTime value, int month, int day)
        {
            return await Task.FromResult(value.IsLunarCalendarHoliday(month, day));
        }

        /// <summary>
        ///   公历是否为农历节假日
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static bool IsLunarCalendarHoliday(this DateTime dt)
        {
            int year = china.GetYear(dt);
            int month = china.GetMonth(dt);
            int leapMonth = china.GetLeapMonth(year);
            int day = china.GetDayOfMonth(dt);

            if (leapMonth != month)
            {
                if (leapMonth != 0 && month >= leapMonth)
                {
                    month--;
                }
                object n = lunarCalendarHoliday[month.ToString("00") + day.ToString("00")];
                return n != null;
            }
            return false;
        }

        /// <summary>
        /// 是否为农历节假日
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        public static async Task<bool> IsLunarCalendarHolidayAsync(this DateTime value)
        {
            return await Task.FromResult(value.IsLunarCalendarHoliday());
        }

        /// <summary>
        ///     是否为清明节
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool IsQingmingFestival(this DateTime value)
        {
            const int QingmingIndex = 6;
            var solarTerm = GetSolarTerm(value);
            return solarTerm == QingmingIndex;
        }

        /// <summary>
        ///     是否为清明节
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static async Task<bool> IsQingmingFestivalAsync(this DateTime value)
        {
            return await Task.FromResult(value.IsQingmingFestival());
        }

        /// <summary>
        /// 获取节气
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static int GetSolarTerm(DateTime dt)
        {
            var solarTerm = -1;
            DateTime dtBase = new DateTime(1900, 1, 6, 2, 5, 0);
            DateTime dtNew;
            double num;
            int y;
            string strReturn = "";

            y = dt.Year;
            for (int i = 1; i <= 24; i++)
            {
                num = 525948.76 * (y - 1900) + JQData[i - 1];
                dtNew = dtBase.AddMinutes(num);
                if (dtNew.DayOfYear == dt.DayOfYear)
                {
                    solarTerm = i - 1;
                }
            }

            return solarTerm;
        }

        /// <summary>
        /// 获取农历月份
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        private static int GetChinaMonth(DateTime dt)
        {
            int year = china.GetYear(dt);
            int month = china.GetMonth(dt);
            int leapMonth = china.GetLeapMonth(year);
            if (leapMonth != 0 && month >= leapMonth)
            {
                month--;
            }

            return month;
        }


    }

    /// <summary>
    /// 二十四节气
    /// </summary>
    public enum SolarTerm
    {

    }

}